home *** CD-ROM | disk | FTP | other *** search
- /* Routines to compute poly normals and bounding sphere
- // Object, object list routines
- // NEW VR-386 support for composite objects,
- // EMM, new recoloring and so on
-
- /* Original module written by Bernie Roehl, December 1991 */
- // (Massive changes since), mostly by Dave Stampe
- // All VR-386 rewrites and integer math by Dave Stampe
-
- // This version support MAP-EMM, and allocates representation memory
- // as one large block. THis requires prescanning of PLG files
-
-
- /*
- This code is part of the VR-386 project, created by Dave Stampe.
- VR-386 is a desendent of REND386, created by Dave Stampe and
- Bernie Roehl. Almost all the code has been rewritten by Dave
- Stampre for VR-386.
-
- Copyright (c) 1994 by Dave Stampe:
- May be freely used to write software for release into the public domain
- or for educational use; all commercial endeavours MUST contact Dave Stampe
- (dstampe@psych.toronto.edu) for permission to incorporate any part of
- this software or source code into their products! Usually there is no
- charge for under 50-100 items for low-cost or shareware products, and terms
- are reasonable. Any royalties are used for development, so equipment is
- often acceptable payment.
-
- ATTRIBUTION: If you use any part of this source code or the libraries
- in your projects, you must give attribution to VR-386 and Dave Stampe,
- and any other authors in your documentation, source code, and at startup
- of your program. Let's keep the freeware ball rolling!
-
- DEVELOPMENT: VR-386 is a effort to develop the process started by
- REND386, improving programmer access by rewriting the code and supplying
- a standard API. If you write improvements, add new functions rather
- than rewriting current functions. This will make it possible to
- include you improved code in the next API release. YOU can help advance
- VR-386. Comments on the API are welcome.
-
- CONTACT: dstampe@psych.toronto.edu
- */
-
-
- #include <stdio.h>
- #include <alloc.h>
- #include <string.h>
- #include <math.h>
- #include <dos.h>
-
- #include "3dstruct.h"
- #include "intmath.h" /* for special sphere test */
- #include "vr_api.h"
- #include "segment.h" // segment operations
- #include "renderer.h"
- #include "xmem.h"
-
-
-
- ///////////////// OBJECT INITIALIZATION /////////////
-
- static void find_bounding_sphere(VISOBJ *obj, REP *rep)
- {
- int i;
- long minx, maxx, miny, maxy, minz, maxz;
- long r;
-
- accessptr(rep->polys); // make sure it's accessible
-
- if (rep == NULL) return;
-
- minx = maxx = rep->verts[0].x;
- miny = maxy = rep->verts[0].y;
- minz = maxz = rep->verts[0].z;
-
- for (i = 1; i < rep->nverts; ++i)
- { /* find bounding cube */
- if (rep->verts[i].x < minx) minx = rep->verts[i].x;
- if (rep->verts[i].y < miny) miny = rep->verts[i].y;
- if (rep->verts[i].z < minz) minz = rep->verts[i].z;
- if (rep->verts[i].x > maxx) maxx = rep->verts[i].x;
- if (rep->verts[i].y > maxy) maxy = rep->verts[i].y;
- if (rep->verts[i].z > maxz) maxz = rep->verts[i].z;
- }
-
- /* compute center of cube */
- obj->osphx = obj->sphx = (maxx - minx) /2 + minx;
- obj->osphy = obj->sphy = (maxy - miny) /2 + miny;
- obj->osphz = obj->sphz = (maxz - minz) /2 + minz;
- /* farthest point from center is the radius of the bounding sphere */
-
- r = 0;
- for (i = 0; i < rep->nverts; ++i)
- {
- r = magnitude32((rep->verts[i].x - obj->sphx),
- (rep->verts[i].y - obj->sphy),
- (rep->verts[i].z - obj->sphz) );
- if (r > obj->sphr) obj->sphr = r;
- }
- }
-
-
- #define NSCALE 536870912 /* 2^29: desired magnitude of normal */
-
- static void compute_normal(POLY *p) /* Compute a polygon's normal */
- { /* uses Dave's fast int math */
- int i = 0; /* so it could be used for morphing */
- int j, k;
- long qinx, qiny, qinz;
- long longest = 0;
- long size;
- VERTEX *s,*e, *ls, *le;
-
- p->onormalx = p->normalx = NSCALE; /* default solution */
- p->onormaly = p->normaly = 0;
- p->onormalz = p->normalz = 0;
-
- if (p->npoints < 3) return; /* fake it for lines */
-
- if (p->npoints == 3) /* only one solution for triangle: ideal for morphing */
- {
- find_normal(p->points[0]->ox,p->points[0]->oy,p->points[0]->oz,
- p->points[1]->ox,p->points[1]->oy,p->points[1]->oz,
- p->points[2]->ox,p->points[2]->oy,p->points[2]->oz,
- &qinx, &qiny, &qinz );
- p->onormalx = p->normalx = qinx;
- p->onormaly = p->normaly = qiny;
- p->onormalz = p->normalz = qinz;
- return;
- }
-
- /* if more sides: find longest cross product*/
- for (i = 0; i < p->npoints-1; i++)
- {
- s = p->points[i];
- e = p->points[i+1];
- size = big_dist(s->ox, s->oy, s->oz, e->ox, e->oy, e->oz);
- if (size > longest)
- {
- longest = size;
- ls = s;
- le = e;
- }
- }
- s = p->points[i];
- e = p->points[0];
- size = big_dist(s->ox, s->oy, s->oz, e->ox, e->oy, e->oz);
- if (size > longest)
- {
- longest = size;
- ls = s;
- le = e;
- }
-
- if (longest == 0) return; // degenerate poly
-
- k = -1; /* now find acceptable point */
- for (i = 0; i < p->npoints; i++)
- {
- s = p->points[i];
- if (s != ls && s != le)
- {
- j = find_normal(ls->ox, ls->oy, ls->oz, le->ox, le->oy, le->oz,
- s->ox, s->oy, s->oz, &qinx, &qiny, &qinz );
-
- if (j > k) /* longer: more precision */
- {
- p->onormalx = p->normalx = qinx;
- p->onormaly = p->normaly = qiny;
- p->onormalz = p->normalz = qinz;
- k = j;
- }
- if (j > 16) return; /* enough vertices tried already */
- }
- }
- }
-
-
- /////////////////// REPRESENTATION CONTROL ////////////////
-
-
- void lock_current_representation(OBJECT *o, WORD n) // forces object to use one rep:
- { // used for morphing animation
- REP *r;
-
- if(!(o=object2visobj(o))) return; // is a segment!
- if (o)
- {
- o->oflags |= OBJ_REPLOCK;
- if(n==0) return;
- r = o->replist;
- while (n-- && r->next) r = r->next;
- o->current_rep = r;
- }
- }
-
-
- void unlock_current_representation(OBJECT *o) // allow rep to be selected by distance again
- {
- if(!(o=object2visobj(o))) return; // is a segment!
- if (o) o->oflags &= ~OBJ_REPLOCK;
- }
-
-
- static void activate_rep(OBJECT *obj, REP *rep)
- {
- if(!(obj=object2visobj(obj))) return; // is a segment!
- obj->current_rep = rep; // make rep the current one: used during lock
- }
-
-
- void select_first_representation(OBJECT *obj) // make first rep the current one: used during lock
- {
- if(!(obj=object2visobj(obj))) return; // is a segment!
- activate_rep(obj, obj->replist);
- }
-
- // make next rep the current one: used during lock
- WORD select_next_representation(OBJECT *obj) /* return 1 if wrapping back to first */
- {
- if(!(obj=object2visobj(obj))) return 0; // is a segment!
- if (obj->current_rep == NULL)
- activate_rep(obj, obj->replist);
- else if ((obj->current_rep)->next == NULL)
- {
- activate_rep(obj, obj->replist);
- return 1;
- }
- else
- {
- activate_rep(obj, (obj->current_rep)->next);
- return 0;
- }
- return 1;
- }
-
- // find representation that will be
- // active at given screen size
- void select_representation(OBJECT *obj, WORD size)
- {
- REP *p;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- for (p = obj->replist; p; p = p->next)
- if (p->size <= size)
- {
- activate_rep(obj, p);
- break;
- }
- }
-
-
- // set size field of rep
- void set_representation_size(OBJECT *obj, WORD size)
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
- rep->size = size;
- }
-
- // read size field
- WORD get_representation_size(OBJECT *obj)
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return 0; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return 0;
- return rep->size;
- }
-
-
-
- /////////////////// DELETE A REPRESENTATION ///////////////
-
- static void del_rep(REP *rep) // delete rep
- {
- if (rep == NULL) return;
-
- if (rep->polys) extfree(rep->polys); // all memory is one big block
- free(rep); // free descriptor too
- }
- // delete current rep of object
- // NOT REALLY SAFE-- LEAVE OUT OF API
- void delete_current_representation(OBJECT *obj) /* needs testing */
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
-
- if (rep == NULL || obj->replist == NULL) return;
- if (rep == obj->replist)
- obj->replist = rep->next;
- else
- { // close up the list!
- REP *r;
- for (r = obj->replist; r->next != NULL; r = r->next)
- if (r->next == rep)
- break;
- if (r->next == NULL) return; /* wasn't in list */
- r->next = rep->next;
- }
- del_rep(rep);
- }
-
-
- ///////////////// REPRESENTATION CREATE AND LOAD DATA //////////
-
-
- static VERTEX **vtxalloc; // used to allocate mem for vtx ptrs
-
- // this has been reorganized to alloc one block of memory
- // for everything. This makes banked memory like EMM usable
- // problem is that we don't usually know the total vertices
- // that each poly will have, so the new argument tpverts was added
- // for this.
-
- REP *add_representation(OBJECT *obj, WORD size, WORD nv, WORD np, WORD tpverts)
- {
- REP *p, *q;
- POLY *pp;
- VERTEX **vpp;
- VERTEX *vp;
- long tmpsize;
-
- if (obj == NULL) return NULL;
- if(!(obj=object2visobj(obj))) return NULL; // is a segment!
-
- if ((p = malloc(sizeof(REP))) == NULL) return NULL;
- p->size = size;
- p->nverts = 0;
- p->npolys = 0; /* used as counters when adding */
- p->update_count = 0;
- p->flags = 0;
-
- tmpsize = nv*sizeof(VERTEX) + // vertices
- tpverts*sizeof(VERTEX *) + // poly vertex counters
- np*sizeof(POLY); // polys
-
- pp = (POLY *)extmalloc(tmpsize); // also makes accessible
- if(pp==NULL)
- {
- free(p);
- return NULL;
- }
- vp = (VERTEX *)(pp+np);
- vtxalloc = (VERTEX **)(vp+nv); // save these to alloc vertex ptrs to polys
- p->polys = pp;
- p->verts = vp;
-
- activate_rep(obj, p); // make rep current so we can process it
- if (obj->replist == NULL) // add to replist of object
- {
- obj->replist = p;
- p->next = NULL;
- return p;
- }
- q = obj->replist; // insert by switching screen size
- if (q->size <= size)
- {
- p->next = q;
- obj->replist = p;
- return p;
- }
- while (q->next)
- {
- if ((q->next)->size <= size) break;
- q = q->next;
- }
- p->next = q->next;
- q->next = p;
- return p;
- }
-
- // adds poly to rep: still needs vertex list defined
- POLY *add_poly(OBJECT *obj, SURFACE color, WORD npoints)
- {
- POLY *p;
-
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return NULL; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return NULL;
-
- accessptr(rep->polys); // ensure access to rep data
-
- p = &(rep->polys[rep->npolys]); // pointer to current poly (last added)
-
- p->points = vtxalloc; // "allocate" vertex memory
- vtxalloc += npoints;
-
- p->object = obj;
- p->color = color;
- p->npoints = 0;
- ++rep->npolys;
- return p;
- }
-
- // adds vertex to object: these aren't yet hooked to polys
- void add_vertex(OBJECT *obj, long x, long y, long z)
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys);
-
- rep->verts[rep->nverts].x = rep->verts[rep->nverts].ox = x;
- rep->verts[rep->nverts].y = rep->verts[rep->nverts].oy = y;
- rep->verts[rep->nverts].z = rep->verts[rep->nverts].oz = z;
- ++rep->nverts;
- }
-
- // connects vertices to polys thru vertex lists
- void add_point(OBJECT *obj, POLY *p, WORD vertnum)
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys);
-
- p->points[p->npoints++] = &(rep->verts[vertnum]);
- }
-
-
-
- ///////////////// OBJECT CREATE AND SUPPORT ///////////
-
- /* allocate an OBJECT */
- // also one representation
- static OBJECT *new_visobj(int nv, int np, int npverts)
- {
- OBJECT *obj;
-
- if ((obj = malloc(sizeof(OBJECT))) == NULL) return NULL;
- obj->oflags = IS_VISOBJ;
- obj->owner = NULL;
- obj->current_rep = obj->replist = NULL;
- if (add_representation(obj, 0, nv, np, npverts) == NULL)
- {
- free(obj);
- return NULL;
- }
- obj->sphx = obj->sphy = obj->sphz = 0;
- obj->osphx = obj->osphy = obj->osphz = 0;
- obj->sphr = 0;
- obj->prev = NULL;
- obj->nnext = NULL;
- obj->update_count = 0;
- return obj;
- }
-
-
- OBJECT *create_invisible_object()
- {
- return new_seg(NULL);
- }
-
-
- OBJECT *create_fixed_object(WORD nv, WORD np, WORD npverts)
- {
- return new_visobj(nv, np, npverts);
- }
-
-
- void delete_visobj(OBJECT *obj) // delete an object and all its reps
- {
- VISOBJ *v = object2visobj(obj);
- REP *rep,*next;
-
- if(!v) return;
- rep = v->replist;
- while (rep)
- {
- next = rep;
- rep = next->next;
- del_rep(next);
- }
- remove_from_objlist(v);
- if(v->owner) seg_reset_object(v->owner, v);
- free(v);
- }
-
-
- OBJECT *create_moveable_object(WORD nv, WORD np, WORD npverts)
- {
- SEGMENT *s;
- VISOBJ *v = new_visobj(nv, np, npverts);
- if(!v) return NULL;
- s = new_seg(NULL);
- if(!s)
- {
- delete_visobj(v);
- return NULL;
- }
- seg_set_object(s, v);
- return v;
- }
-
-
- OBJECT *make_fixed_object_moveable(OBJECT *obj, OBJECT *invis)
- {
- SEGMENT *s = object2segment(invis);
- VISOBJ *v = object2visobj(obj);
- if(!v) return NULL;
- if(v->owner) return v->owner; // already moveable!
- if(!s) s = new_seg(NULL); // no link supplied
- if(!s)
- {
- delete_visobj(v);
- return NULL;
- }
- seg_set_object(s, v);
- return v;
- }
-
- // disconnects visible part of object from
- // invisible object, returns visible part.
- // invisible part is stored in **invis.
- // <preserve> sets whether to leave fixed
- // object in same world positon-- DON'T USE
- // if you are going to make object moveable again!
- OBJECT *make_moveable_object_fixed(OBJECT *obj, OBJECT **invis, BOOL preserve)
- {
- SEGMENT *s = object2segment(obj);
- VISOBJ *v = object2visobj(obj);
-
- if(invis) *invis = s;
- if(!v) return NULL;
- if(!s) return v;
- if(preserve)
- {
- REP *r = v->current_rep;
- REP *n = v->replist;
-
- update_segment(s);
- while(n) // convert all reps to world coords
- {
- v->current_rep = n;
- accessptr(n->polys);
- matmove_rep(n, get_seg_pmatrix(s));
- copy_world_to_object(v);
- n = n->next;
- }
- }
- seg_reset_object(s,v);
- return v;
- }
-
-
-
- OBJECT *convert_objlist_to_moveable_object(OBJLIST *olist)
- {
- VISOBJ *robj, *xobj, *obj = first_in_objlist(olist);
- WORD oflags;
-
- if(!obj) return NULL; // no objects!
-
- if(!next_in_objlist(obj)) // only one object: make moveable
- {
- return make_fixed_object_moveable(obj,NULL);
- }
- else // many objects: join together
- {
- robj = create_invisible_object();
- if(!robj) return NULL;
- xobj = make_fixed_object_moveable(obj,NULL);
- naked_attach_object(xobj, robj); // be sure NOT moved off list!
- while((obj=next_in_objlist(obj))!=NULL)
- {
- xobj = make_fixed_object_moveable(obj,NULL);
- naked_attach_object(xobj, robj); // be sure NOT moved off list!
- }
- }
- return robj;
- }
-
-
- // can delete segments or visobjs, but not if
- // the segment is system owned (camera, light)
- void delete_object(OBJECT *obj)
- {
- SEGMENT *s = object2segment(obj); // get parts
- VISOBJ *v = object2visobj(obj);
-
- if(v) delete_visobj(v); // delete visobj
- if(s)
- if(!(is_system_owned(s))) // delete segment if possible
- destroy_segment(s);
- }
-
-
- /////////////////// OBJECT MANIPULATION ////////////
-
- // set object flag bits
- void set_obj_flags(OBJECT *obj, unsigned int val)
- {
- obj->oflags = (val & OBJ_FLAG_MASK) |
- (obj->oflags & (~OBJ_FLAG_MASK)) | IS_VISOBJ;
- }
- // read object flag bits
- unsigned get_obj_flags(OBJECT *obj)
- {
- return obj->oflags & OBJ_FLAG_MASK;
- }
- // read bounding sphere of object
-
- long get_object_bounds(OBJECT *obj, long *x, long *y, long *z)
- {
- if(!(obj=object2visobj(obj))) return 0; // is a segment!
- if (x) *x = obj->sphx;
- if (y) *y = obj->sphy;
- if (z) *z = obj->sphz;
- return obj->sphr;
- }
-
- // computes renderer data for object
- void compute_object(OBJECT *obj) /* for current rep only! */
- {
- int i;
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
-
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys); // make sure data is accessible!
-
- for (i = 0; i < rep->npolys; ++i)
- compute_normal(&rep->polys[i]);
-
- find_bounding_sphere(obj, rep);
- }
-
- // computes renderer data for object
- void compute_all_object(OBJECT *obj) /* for all reps */
- {
- int i;
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
-
- rep = obj->replist;
- if (rep == NULL) return;
- obj->sphr = 0;
- while (rep)
- {
- obj->current_rep = rep;
-
- accessptr(rep->polys);
-
- for (i = 0; i < rep->npolys; ++i)
- compute_normal(&rep->polys[i]);
-
- find_bounding_sphere(obj, rep);
- rep = rep->next;
- }
- }
-
-
-
- ///////////////// OBJECT LIST MANAGEMENT //////////
-
- // NOTES ON OBJECT LIST
- // Why is there an object list with a special header?
- // the header serves to terminate the head of the
- // double-linked object list, so that the object list
- // is easier to add or delete from (the only special
- // case is the header, and it just looks like another
- // object with a NULL prev ptr. The double-linked list
- // was introduced by Dave to allow the quick insertion
- // and deletion of objects from lists, critical since
- // moving objects often change object lists when splits
- // are used.
-
- // OBJECT LISTS contain only VISOBJs, and are used for
- // visibility only (in split trees)
-
-
- OBJLIST *new_objlist()
- {
- OBJLIST *p;
-
- p = malloc(sizeof(OBJLIST)); // allocate a header
- if(p)
- {
- p->nnext = NULL; // set to empty
- p->prev = NULL;
- p->oflags = OBJLIST_HEADER;
- }
- return p;
- }
-
-
- BOOL on_any_objlist(OBJECT *obj) // is it currently in a list?
- {
- if (obj == NULL) return FALSE;
- if(!(obj=object2visobj(obj))) return FALSE; // is a segment!
- return (obj->prev!=NULL);
- }
-
- // drop from list, needed often
- void remove_from_objlist(OBJECT *obj)
- {
- if (obj == NULL) return;
- if(!(obj=object2visobj(obj))) return; // is a segment!
- if (obj->prev == NULL) return; // object list header, or not on objlist
-
- if (obj->nnext != NULL) obj->nnext->prev = obj->prev;
- obj->prev->nnext = obj->nnext;
-
- obj->nnext = obj->prev = NULL;
- }
-
-
- // put object at head of list
- // remove if already on any list
- void add_to_objlist(OBJLIST *list, OBJECT *obj)
- {
- if (list == NULL || obj == NULL) return;
- if(!(obj=object2visobj(obj))) return; // is a segment!
-
- if(obj->prev) remove_from_objlist(obj);
- if (list->nnext == NULL)
- obj->nnext = NULL; /* first in empty list */
- else
- {
- list->nnext->prev = obj; // else add before first
- obj->nnext = list->nnext;
- }
- list->nnext = obj;
- obj->prev = (OBJECT *) list;
- }
-
-
- void delete_objlist_and_objects(OBJLIST *list)
- {
- if (list) // delete list, and all objects in it
- {
- while (list->nnext) // does this work????
- {
- OBJECT *obj;
- obj = list->nnext;
- remove_from_objlist(list->nnext);
- delete_object(obj);
- }
- if(!(list->oflags&SYSTEM_OWNED)) free(list);
- }
- }
-
- void delete_objlist_keeping_objects(OBJLIST *list)
- {
- if (list) // delete list, and all objects in it
- {
- while (list->nnext) // does this work????
- {
- remove_from_objlist(list->nnext);
- }
- if(!(list->oflags&SYSTEM_OWNED)) free(list);
- }
- }
-
- // removes all from <from>, adds to start of <to>
- void merge_objlists(OBJLIST *from, OBJLIST *to)
- {
- VISOBJ *vt = to, *vf = from;
-
- if(!vf->nnext) return; // none to move
-
- if(!vt->nnext) // empty to list
- {
- vt->nnext = vf->nnext; // just move it all
- vf->nnext->prev = vt;
- vf->nnext = NULL;
- }
-
- while(vf->nnext) vf = vf->nnext; // find end of list
- vt->nnext->prev = vf; // splice lists
- vf->nnext = vt->nnext;
- vf->nnext->prev = vt;
- vf->nnext = NULL;
- }
-
-
- OBJLIST *which_objlist(OBJECT *obj) // look for head of list object is
- { // in. Kinda slow for big lists
- if (obj == NULL) return NULL;
- if(!(obj=object2visobj(obj))) return NULL; // is a segment!
- for (obj = obj->prev; obj->prev; obj = obj->prev);
- return (OBJLIST *) obj;
- }
-
-
-
- /* Additional functions suggested by wendellj@microsoft.com */
-
- OBJECT *first_in_objlist(OBJLIST *objlist)
- {
- return objlist->nnext;
- }
-
- OBJECT *next_in_objlist(OBJECT *obj)
- {
- if(!(obj=object2visobj(obj))) return NULL; // is a segment!
- return obj->nnext;
- }
-
- OBJECT *prev_in_objlist(OBJECT *obj)
- {
- if(!(obj=object2visobj(obj))) return NULL; // is a segment!
- return obj->prev;
- }
-
- BOOL is_first_in_objlist(OBJECT *obj)
- {
- if(!(obj=object2visobj(obj))) return FALSE; // is a segment!
- return obj->prev->prev == NULL;
- }
-
- BOOL is_last_in_objlist(OBJECT *obj)
- {
- if(!(obj=object2visobj(obj))) return FALSE; // is a segment!
- return obj->nnext == NULL;
- }
-
-
- // total number of vertices of all polys NEW NEW NEW
- WORD total_object_pverts(OBJECT *obj)
- {
- int i,j;
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return 0; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return 0;
-
- accessptr(rep->polys);
-
- j = 0;
- for(i=0;i<rep->npolys;i++)
- j += rep->polys[i].npoints;
- return j;
- }
-
- // current rep info
- void get_obj_info(OBJECT *obj, int *nv, int *np)
- {
- REP *rep;
- VISOBJ *v;
-
- if(!(v=object2visobj(obj))) return; // is a segment!
- rep = v->current_rep;
- if (rep == NULL) return;
-
- if (nv) *nv = rep->nverts;
- if (np) *np = rep->npolys;
- }
-
-
-
- // This is another useful function.
- // You can use it to perform an
- // operation on every object in a
- // list with one call. Speed? What speed?
- // this is useful with splits for walking
- // all visible objects in the environment
-
-
- void walk_objlist(OBJLIST *objlist, void (*fn)(OBJECT *))
- {
- OBJECT *obj;
- OBJECT *next;
- /* DS fix: so objects may be deleted by the fn() ! */
- if (objlist == NULL) return;
- obj = objlist->nnext;
- while (obj != NULL)
- {
- next = obj->nnext; /* get ahead of time in case object is modified */
- fn(obj);
- obj = next;
- }
- }
-
-
- void get_vertex_world_info(OBJECT *obj, WORD vertnum, COORD *x, COORD *y, COORD *z)
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys); // make sure we can acceess data
-
- if (x) *x = rep->verts[vertnum].x;
- if (y) *y = rep->verts[vertnum].y;
- if (z) *z = rep->verts[vertnum].z;
- }
-
- void get_vertex_info(OBJECT *obj, WORD vertnum, COORD *x, COORD *y, COORD *z)
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys); // make sure we can acceess data
-
- if (x) *x = rep->verts[vertnum].ox;
- if (y) *y = rep->verts[vertnum].oy;
- if (z) *z = rep->verts[vertnum].oz;
- }
-
- void set_vertex_coords(OBJECT *obj, int vertnum, long x, long y, long z)
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys); // make sure we can acceess data
-
- rep->verts[vertnum].ox = rep->verts[vertnum].x = x;
- rep->verts[vertnum].oy = rep->verts[vertnum].y = y;
- rep->verts[vertnum].oz = rep->verts[vertnum].z = z;
- }
-
- WORD get_poly_vertex_index(OBJECT *obj, WORD poly, WORD vertex)
- {
- int i, n;
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return 0; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return 0;
-
- accessptr(rep->polys); // make sure we can acceess data
-
- return (FP_OFF(rep->polys[poly].points[vertex]) -
- FP_OFF(rep->verts)) /sizeof(VERTEX);
- }
-
-
- // get data on poly
- void get_poly_info(OBJECT *obj, WORD polynum, SURFACE *color, WORD *nverts)
- {
- int i, n;
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys); // make sure we can acceess data
-
- if (color) *color = rep->polys[polynum].color;
- n = rep->polys[polynum].npoints;
- if (nverts) *nverts = n;
- }
-
-
- void set_poly_color(OBJECT *obj, WORD polynum, SURFACE color)
- {
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys); // make sure we can acceess data
-
- if (polynum < rep->npolys)
- rep->polys[polynum].color = color;
- }
-
-
- /////// OBJECT RECOLORING
-
- // very flexible remaps colors of object
- void masked_remap_object_surface(OBJECT *obj, SURFACEPAIR map[20], WORD nmap,
- SURFACE sbitmask, SURFACE dbitmask, BOOL all)
- {
- REP *rep;
- int i,j;
- SURFACE color;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- if (all) select_first_representation(obj);
- do {
- rep = obj->current_rep;
- accessptr(rep->polys);
- for(i=0;i<rep->npolys;i++)
- {
- color = rep->polys[i].color;
- for(j=0;j<nmap;j++)
- {
- if((color&sbitmask)==(map[j].old&sbitmask))
- {
- color = (color&(~dbitmask))|(map[j].new&dbitmask);
- break;
- }
- }
- rep->polys[i].color = color;
- }
- } while(all && !select_next_representation(obj));
- }
-
- // flexible recolo/resurface object
- void masked_recolor_object_surface(OBJECT *obj, SURFACE new,
- SURFACE bitmask, BOOL all)
- {
- REP *rep;
- int i;
- SURFACE color;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- if (all) select_first_representation(obj);
- do {
- rep = obj->current_rep;
- accessptr(rep->polys);
- for(i=0;i<rep->npolys;i++)
- {
- color = rep->polys[i].color;
- color = (color&(~bitmask))|(new&bitmask);
- rep->polys[i].color = color;
- }
- } while(all && !select_next_representation(obj));
- }
-
-
- // very flexible remaps colors
- // does for all objects in objlist
- void objlist_remap_surface(OBJLIST *olist, SURFACEPAIR map[20], WORD nmaps,
- SURFACE sbitmask, SURFACE dbitmask)
- {
- OBJECT *obj = first_in_objlist(olist);
-
- if(nmaps && obj)
- {
- masked_remap_object_surface(obj, map, nmaps, sbitmask, dbitmask, 0);
- while((obj=next_in_objlist(obj))!=NULL)
- masked_remap_object_surface(obj, map, nmaps, sbitmask, dbitmask, 0);
- }
- }
-
-
- /* find object that point is closest to center of */
- /* this is used with the Powerglove to select objects */
-
- OBJECT *best_collision(OBJLIST *objlist, long x, long y, long z)
- {
- OBJECT *obj;
- OBJECT *bestobj = NULL;
- long mindist = 0x7FFFFFFF;
- long dist;
-
- if (objlist == NULL) return NULL;
- for (obj = objlist->nnext; obj; obj = obj->nnext)
- { /* not selectable... skip */
- if (obj->oflags & (OBJ_INVIS|OBJ_NONSEL)) continue;
- if (obj->owner == NULL) continue; /* ignore if no segment */
- dist = sphere_pretest(obj, x, y, z); /* test object */
- if (dist < mindist)
- {
- mindist = dist; /* better fit: accept */
- bestobj = obj;
- }
- }
- return bestobj;
- }
-
- // set highlight flags in object
- void highlight_object(OBJECT *obj)
- {
- int i;
-
- if (obj == NULL) return;
- if(!(obj=object2visobj(obj))) return; // is a segment!
- obj->oflags |= OBJ_HIGHLIGHTED;
- }
-
-
- void unhighlight_object(OBJECT *obj)
- {
- int i;
-
- if (obj == NULL) return;
- if(!(obj=object2visobj(obj))) return; // is a segment!
- obj->oflags &= ~OBJ_HIGHLIGHTED;
- }
-
- void make_object_unselectable(OBJECT *obj)
- {
- int i;
-
- if (obj == NULL) return;
- if(!(obj=object2visobj(obj))) return; // is a segment!
- obj->oflags |= OBJ_NONSEL;
- }
-
- void mark_object_invisible(OBJECT *obj)
- {
- int i;
-
- if (obj == NULL) return;
- if(!(obj=object2visobj(obj))) return; // is a segment!
- obj->oflags |= OBJ_INVIS;
- }
-
- void mark_object_visible(OBJECT *obj)
- {
- int i;
-
- if (obj == NULL) return;
- if(!(obj=object2visobj(obj))) return; // is a segment!
- obj->oflags &= ~OBJ_INVIS;
- }
-
- // set/clear flags in visobj part of object
- void set_object_flags(OBJECT *obj, WORD flagmask, WORD val)
- {
- if(!(obj=object2visobj(obj))) return; // is a segment!
- if(val) obj->oflags |= flagmask;
- else obj->oflags &= ~flagmask;
- }
-
-
-
-
- ///////////////////// SCREEN POINT MONITOR SUPPORT //////////////
-
-
- // NOTE: the vertex returned by these (and the screen monitor) is
- // the sequential vertex IN THE POLY, NOT the object vertex index.
-
- // see what object's on the screen
- // at the given point <in cursor2d.c>
- extern OBJECT *find_object_on_screen(WORD x, WORD y);
-
- // ASSUMES YOU'VE USED DONE A REFRESH WITH MONITOR
-
- static SWORD screen_obj_poly; // index of poly in object (-1 if none)
- static SWORD screen_obj_vert; // index of vertex in object (-1 if none)
- static SWORD screen_poly_vert; // index of vertex in poly (-1 if none)
-
- void process_screen_monitor()
- {
- OBJECT *obj;
- POLY *p;
- int pi;
-
- screen_obj_poly = screen_obj_vert = screen_poly_vert = -1;
-
- obj = screen_monitor_object();
- if (obj)
- {
- p = screen_monitor_poly();
- if (p == NULL) return;
- screen_obj_poly = (FP_OFF(p) - FP_OFF(obj->current_rep->polys)) /sizeof(POLY);
- screen_poly_vert = screen_monitor_vertex();
- if(screen_poly_vert!=-1)
- screen_obj_vert = get_poly_vertex_index(obj, screen_obj_poly,
- screen_poly_vert);
- }
- }
-
- OBJECT *screen_found_object()
- {
- return screen_monitor_object();
- }
-
- SWORD screen_found_poly()
- {
- return screen_obj_poly;
- }
-
- SWORD screen_found_poly_vertex()
- {
- return screen_poly_vert;
- }
-
- SWORD screen_found_object_vertex()
- {
- return screen_obj_vert;
- }
-
-
-
-
-
-
- ////////////////// MORE DATA-HIDING ROUTINES //////////
-
-
- SEGMENT *object2segment(OBJECT *obj)
- {
- if(!obj) return NULL;
- if(is_object_segment(obj)) return obj;
- return ((VISOBJ *)obj)->owner;
- }
-
-
- unsigned get_object_sorting(OBJECT *obj)
- {
- if(!(obj=object2visobj(obj))) return 0; // is a segment!
-
- return obj->oflags & OBJ_DEPTH_MASK;
- }
-
- void set_object_sorting(OBJECT *obj, unsigned depth_type)
- {
- if(!(obj=object2visobj(obj))) return; // is a segment!
- obj->oflags = (depth_type & OBJ_DEPTH_MASK)
- | (obj->oflags & (~OBJ_DEPTH_MASK))
- | IS_VISOBJ;
- }
-
-
- /************** MOVE OBJECT COMPLETELY ***********/
-
- void copy_world_to_object(OBJECT *obj) // used to "lock" fixed objects
- { // on loading
- int i;
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys);
-
- for (i = 0; i < rep->nverts; ++i)
- {
- rep->verts[i].ox = rep->verts[i].x;
- rep->verts[i].oy = rep->verts[i].y;
- rep->verts[i].oz = rep->verts[i].z;
- }
- }
-
-
- void scale_object(OBJECT *obj, float sx, float sy, float sz)
- {
- int i;
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if (rep == NULL) return;
-
- accessptr(rep->polys);
-
- for (i = 0; i < rep->nverts; ++i)
- {
- rep->verts[i].x = rep->verts[i].ox = rep->verts[i].ox*sx;
- rep->verts[i].y = rep->verts[i].oy = rep->verts[i].oy*sy;
- rep->verts[i].z = rep->verts[i].oz = rep->verts[i].oz*sz;
- }
- }
-
-
- void apply_matrix(OBJECT *obj, MATRIX m) // moves object completely
- { // useful on loading
- REP *rep;
-
- if(!(obj=object2visobj(obj))) return; // is a segment!
- rep = obj->current_rep;
- if(rep==NULL) return;
-
- accessptr(rep->polys);
-
- matmove_osphere(obj, m);
- matmove_rep(rep, m);
- }
-
-
- void move_rep(VISOBJ *obj) /* move current rep of object */
- { /* called from renderer itself */
- REP *rep = obj->current_rep;
- SEGMENT *s = obj->owner;
-
- accessptr(rep->polys);
-
- if(s) matmove_rep(rep, get_seg_pmatrix(s));
-
- rep->update_count = obj->update_count; // mark as current
- }
-
-
-
-